package org.liurb.springboot.advance.demo.class3.aop;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.liurb.springboot.advance.demo.class3.annotation.MyRedisCache;
import org.liurb.springboot.advance.demo.utils.CoreRedisUtil;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * Redis 缓存切面处理
 *
 * @Author Liurb
 * @Date 2022/12/3
 */
@Aspect
@Component
@Slf4j
@Order(11)
public class MyRedisCacheAspect {

    @Resource
    CoreRedisUtil coreRedisUtil;

    /**
     * 定义切入点为 MyRedisCache 注解
     */
    @Pointcut("@annotation(org.liurb.springboot.advance.demo.class3.annotation.MyRedisCache))")
    public void redisCachePointcut() {
    }

    /**
     * 环绕通知
     *
     * 可以用来在调用一个具体方法前(判断缓存是否存在)和调用后(设置缓存)来完成一些具体的任务
     *
     * @param joinPoint
     * @param myRedisCache
     * @return
     * @throws Throwable
     */
    @Around("redisCachePointcut() && @annotation(myRedisCache)")
    public Object doAround(ProceedingJoinPoint joinPoint, MyRedisCache myRedisCache) throws Throwable {

        //统一的缓存前缀
        StringBuilder redisKeySb = new StringBuilder("my_redis_cache").append(":");

        //注解上定义的redis key
        String key = myRedisCache.key();

        if (StrUtil.isBlank(key)) {
            throw new RuntimeException("key 不能为空");
        }

        //获取el表达式的key
        String elKey = this.elKey(joinPoint, key);
        //拼接key
        redisKeySb.append(elKey);

        String redisKey = redisKeySb.toString();

        //查缓存
        Object result = coreRedisUtil.get(redisKey);
        if (result != null) {//存在缓存

            if (result instanceof String) {//缓存一般为json字符串，所以这里需要进行返回类型的转换
                String jsonText = result.toString();
                //获取接口的返回值
                Class returnType = this.getReturnType(joinPoint);
                //转换到对应的类型
                return JSON.parseObject(jsonText, returnType);
            }

        }

        //缓存不存在
        try {
            //执行方法
            result = joinPoint.proceed();

        } catch (Throwable e) {
            //方法抛异常
            throw new RuntimeException(e.getMessage(), e);
        }

        //判断是否为null
        if (result != null) {

            String successFiled = myRedisCache.successFiled();
            if (StrUtil.isNotBlank(successFiled)) {//需要判断结果是否成功

                if (isSuccess(result, successFiled) == false) {//返回结果不成功
                    return result;
                }

            }

            //设置失效时间(秒)
            long expire = myRedisCache.expire();
            //设置缓存
            coreRedisUtil.set(redisKey, JSON.toJSONString(result), Duration.ofSeconds(expire));
        }

        //返回结果
        return result;
    }

    /**
     * 获取el表达式的redis key
     *
     * @param joinPoint
     * @param key
     * @return
     */
    private String elKey(ProceedingJoinPoint joinPoint, String key) {
        // 表达式上下文
        EvaluationContext context = new StandardEvaluationContext();

        String[] parameterNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); // 参数名
        Object[] args = joinPoint.getArgs(); // 参数值

        for (int i=0; i<args.length; i++) {//设置evaluation提供上下文变量
            context.setVariable(parameterNames[i], args[i]);
        }
        // 表达式解析器
        ExpressionParser parser = new SpelExpressionParser();
        // 解析
        String redisKey = parser.parseExpression(key).getValue(context, String.class);

        return redisKey;
    }

    /**
     * 获取方法的返回值的类型
     *
     * @param joinPoint
     * @return
     */
    private Class getReturnType(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取method对象
        Method method = signature.getMethod();
        //获取方法的返回值的类型
        Class  returnType = method.getReturnType();

        return returnType;
    }

    /**
     * 判断返回结果是否为成功
     *
     * @param result
     * @param successFiled
     * @return
     */
    private boolean isSuccess(Object result, String successFiled) {

        // 表达式上下文
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("result", result);

        // 表达式解析器
        ExpressionParser parser = new SpelExpressionParser();

        return parser.parseExpression(successFiled).getValue(context, Boolean.class);
    }

}
